// 标签，属性， children,得到元素节点
import { isObject, isReservedTag } from "../util";

export function createElement(vm, tag, data = {}, ...children) {
  // 需要对标签作区分，有可能是一个自定义组件,也可能是一个原生标签
  if (isReservedTag(tag)) {
    // 原生标签
    return vnode(vm, tag, data, data.key, children, undefined);
  } else {
    // 自定义组件, vm.$options 是合并后的结果
    // Ctor 如果是全局组件就是个构造函数，如果是组件内的选项，就是个对象
    const Ctor = vm.$options.components[tag];
    return createComponent(vm, tag, data, data.key, children, Ctor);
  }
}

// 对组件的处理
function createComponent(vm, tag, data, key, children, Ctor) {
  if (isObject(Ctor)) {
    // 经过 mergeOptions 后 vm.$options 拥有 Vue.$options 的属性
    Ctor = vm.$options._base.extend(Ctor);
  }
  // 组件的 data 上有个 hook 属性
  data.hook = {
    init(vnode) {
      // 调用组建的饿构造函数得到一个组件
      let child = (vnode.componentInstance = new vnode.componentOptions.Ctor(
        {}
      ));
      child.$mount(); // child 和 vnode.componentInstance 都会得到一个 $el 属性
    },
  };

  // 组件的 vnode中，data 有hook ,以及 componentOptions 中存放的构造函数
  // vue-component-2-my-button
  return vnode(
    vm,
    `vue-component-${Ctor.cid}-${tag}`,
    data,
    key,
    undefined,
    undefined,
    { Ctor } // 只有组件才有这个参数
  );
}

// 创建文本虚拟节点, 只有 vm 和 文本
export function createTextVnode(vm, text) {
  return vnode(vm, undefined, undefined, undefined, undefined, text);
}

// 将元素节点和文本节点最终实现统一，都变成有共同属性的 vnode 对象
function vnode(vm, tag, data, key, children, text, componentOptions) {
  return {
    vm,
    tag,
    data,
    key,
    children,
    text,
    componentOptions,
  };
}
